package org.javaboy.security_role_permission.config;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;

import java.util.Collection;
import java.util.function.Supplier;

@Configuration
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig {

    @Bean
    RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
        hierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
        hierarchy.setHierarchy("ROLE_ADMIN > ROLE_ROOT");
        return hierarchy;
    }

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(a -> a
                        //无论是 hasAuthority 还是 hasRole，最终底层都是调用了相同的方法。
                        //这两个方法的区别就是 hasRole 会自动为传入的参数添加 ROLE_ 前缀，而 hasAuthority 不会。
//                        .requestMatchers(HttpMethod.GET,"/book").hasAuthority("book:read")
//                        .requestMatchers(HttpMethod.POST,"/book").hasAuthority("book:insert")
//                        .requestMatchers(HttpMethod.PUT,"/book").hasAuthority("book:update")
//                        .requestMatchers(HttpMethod.DELETE,"/book").hasRole("admin")
//                        .requestMatchers(HttpMethod.DELETE,"/book").hasAuthority("book:delete")
                        //自定义访问路径的判断规则
                        .anyRequest().access(new AuthorizationManager<RequestAuthorizationContext>() {
                            @Override
                            public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext object) {
                                //获取当前请求
                                HttpServletRequest request = object.getRequest();
                                //获取请求的路径
                                String requestURI = request.getRequestURI();
                                //获取当前用户具备的角色/权限
                                Collection<? extends GrantedAuthority> authorities = authentication.get().getAuthorities();
                                //此时，就可以根据请求路径去数据库中查询当前请求需要什么样的角色/权限
                                //接下来判断当前用户是否具备这些权限和角色
                                //如果具备，则返回授权
                                //return new AuthorizationDecision(true);
                                //如果不具备，则返回拒绝
                                //return new AuthorizationDecision(false);
                                return null;
                            }
                        }))
                .formLogin(Customizer.withDefaults())
                .exceptionHandling(e -> e.accessDeniedHandler((request, response, accessDeniedException) -> {
                    response.setContentType("text/html;charset=utf-8");
                    response.getWriter().write("没有权限");
                }))
                .csrf(c -> c.disable());
        return http.build();
    }
}
